/**
 * 
 */
package com.huarui.desktop.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import com.huarui.desktop.model.Module;
import com.huarui.desktop.model.ModuleLauncher;
import com.huarui.desktop.model.ModuleLauncherAnchor;
import com.huarui.desktop.service.ModuleFactory;
import com.huarui.user.model.User;

/**
 * @author sunpeng
 * 
 */
public class XmlConfiguredModuleFactory implements ModuleFactory,
		InitializingBean {

	private Map<String, String> params = new HashMap<String, String>();
	private Map<String, Pattern> pattern = new HashMap<String, Pattern>();
	private Resource[] locations;
	private List<Module> modules;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.huarui.desktop.service.ModuleFactory#getAuthorizedModules(com.huarui
	 * .user.model.User, java.lang.String)
	 */
	@Override
	public List<Module> getAuthorizedModules(User user) {
		return this.modules;
	}

	@Override
	public void afterPropertiesSet() throws Exception {

		this.modules = new ArrayList<Module>();
		for (int i = 0; i < locations.length; i++) {
			try {
				parseModules(locations[i], this.modules);
			} catch (RuntimeException ex) {
				throw new NestedIOException("Failed to parse config resource: "
						+ locations[i], ex.getCause());
			}
		}

	}

	public static void main(String[] a) throws Exception {
		ClassPathResource r = new ClassPathResource("/module/modules.xml");
		XmlConfiguredModuleFactory f = new XmlConfiguredModuleFactory();
		f.modules = new ArrayList<Module>();
		f.params = new HashMap<String, String>();
		f.params.put("scriptRoot",
				"http://localhost:8080/huarui/htdocs/desktop/modules");
		f.parseModules(r, f.modules);
		System.out.println(f.modules.size());
		System.out.println(f.modules.get(0).getScriptUrl());
	}

	@SuppressWarnings("unchecked")
	protected void parseModules(Resource resource, List<Module> modules)
			throws Exception {
		SAXBuilder builder = new SAXBuilder();

		Document doc = builder.build(resource.getInputStream());
		Element root = doc.getRootElement();
		Element element, subElement;
		List list = root.getChildren("param");
		Map<String, String> properties = new HashMap<String, String>(
				this.params);
		for (Object obj : list) {
			element = (Element) obj;
			this.putParams(properties, checkParam(element, "name", properties),
					checkParam(element, "value", properties));
		}
		Module module;
		Attribute attribute;
		ModuleLauncher launcher;
		Map<String, String> params;
		Map<ModuleLauncherAnchor, String> launcherPaths;
		list = root.getChildren("module");
		for (Object obj : list) {
			element = (Element) obj;
			module = new Module();
			module.setId(this.checkStringProperty(element, "id", properties,
					true));
			module.setClassName(this.checkStringProperty(element, "className",
					properties, true));
			module.setScriptUrl(this.checkStringProperty(element,
					"scriptUrl", properties, false));
			module.setType(this.checkStringProperty(element, "type",
					properties, false));
			module.setStylesheetUrl(this.checkStringProperty(element,
					"stylesheetUrl", properties, false));
			module.setAutorun(this.checkBooleanProperty(element, "autorun",
					properties));
			module.setInQuickstart(this.checkBooleanProperty(element,
					"inQuickstart", properties));
			module.setInShortcut(this.checkBooleanProperty(element,
					"inShortcut", properties));
			module.setMarkTask(this.checkBooleanProperty(element,
					"markTask", properties,true));
			
			subElement = element.getChild("launcher");
			if (subElement != null) {
				launcher = new ModuleLauncher();
				launcher.setIconCls(parseAttributeValue(subElement
						.getAttributeValue("iconCls"), properties));
				launcher.setShortcutIconCls(parseAttributeValue(subElement
						.getAttributeValue("shortcutIconCls"), properties));
				launcher.setText(parseAttributeValue(subElement
						.getAttributeValue("text"), properties));
				launcher.setTooltip(parseAttributeValue(subElement
						.getAttributeValue("tooltip"), properties));
				module.setLauncher(launcher);

				subElement = subElement.getChild("path");
				if (subElement != null) {
					launcherPaths = new HashMap<ModuleLauncherAnchor, String>();
					if ((attribute = subElement
							.getAttribute(ModuleLauncherAnchor.startmenu.name())) != null) {
						launcherPaths.put(ModuleLauncherAnchor.startmenu,
								parseAttributeValue(attribute.getValue(),
										properties));
					}
					if ((attribute = subElement
							.getAttribute(ModuleLauncherAnchor.startmenutool
									.name())) != null) {
						launcherPaths.put(ModuleLauncherAnchor.startmenutool,
								parseAttributeValue(attribute.getValue(),
										properties));
					}
					module.setLauncherPaths(launcherPaths);
				}

			}

			element = element.getChild("params");
			if (element != null) {
				params = new HashMap<String, String>();
				for (Object subObj : element.getChildren("param")) {
					subElement = (Element) subObj;

					params.put(checkParam(subElement, "name", properties),
							checkParam(subElement, "value", properties));
				}
				module.setInitParams(params);
			}

			modules.add(module);
		}

	}

	protected String checkParam(Element element, String name,
			Map<String, String> properties) {
		String value = element.getAttributeValue(name);
		if (StringUtils.isEmpty(name)) {
			throw new RuntimeException("param '" + name + "' is empty.");
		}
		return parseAttributeValue(value, properties);
	}

	protected boolean checkBooleanProperty(Element element, String name,
			Map<String, String> properties,boolean defValue) {
		String value = element.getAttributeValue(name);
		if (StringUtils.isEmpty(value)) {
			return defValue;
		}

		return Boolean.parseBoolean(parseAttributeValue(value, properties));
	}
	
	protected boolean checkBooleanProperty(Element element, String name,
			Map<String, String> properties) {
		String value = element.getAttributeValue(name);
		if (StringUtils.isEmpty(value)) {
			return false;
		}

		return Boolean.parseBoolean(parseAttributeValue(value, properties));
	}

	protected String checkStringProperty(Element element, String name,
			Map<String, String> properties, boolean checkEmpty) {
		String value = element.getAttributeValue(name);
		if (StringUtils.isEmpty(value)) {
			if (checkEmpty) {
				throw new RuntimeException("module property '" + name
						+ "' is empty.");
			} else {
				return value;
			}
		}

		return parseAttributeValue(value, properties);
	}

	protected String parseAttributeValue(String value,
			Map<String, String> properties) {
		if (value == null) {
			return null;
		}
		int s = -1, e = 0;
		String param;
		while ((s = value.indexOf("${", e)) >= 0) {
			e = value.indexOf('}');
			if (e > s + 2) {
				param = value.substring(s + 2, e);
				if (properties.containsKey(param)) {
					value = this.pattern.get(param).matcher(value)
							.replaceFirst(properties.get(param));
					// value = value.replaceFirst("\\$\\{" + param +
					// "\\}",properties.get(param));
				}

			} else {
				return value;
			}
		}
		return value;
	}

	protected void putParams(Map<String, String> properties, String name,
			String value) {
		properties.put(name, value);
		if (!this.pattern.containsKey(name)) {
			this.putPattern(name);
		}
	}

	protected void putPattern(String name) {
		this.pattern.put(name, Pattern.compile("\\$\\{" + name + "\\}"));
	}

	public void setLocation(Resource location) {
		this.locations = (location != null ? new Resource[] { location } : null);
	}

	public void setLocations(Resource[] locations) {
		this.locations = locations;
	}

	public void setParams(Map<String, String> params) {
		this.params = params;
		if (params != null) {
			for (String p : params.keySet()) {
				this.putPattern(p);
			}
		}
	}

}
